
`include "common_header.verilog" 

//  *************************************************************************
//  File : r_fifo_1234_wl
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2002-2003-2004 Morethanip
//  An der Steinernen Brueke 1, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Peter Riedl
//  info@morethanip.com
//  *************************************************************************
//  Description: Programmable (Asynchronous / Synchronous) FIFO
//               Write pointer is delayed to guarantee
//               safe read when rclk > 2x wclk.
//  Version    : $Id: r_fifo_1234_wl.v,v 1.2 2017/04/18 08:53:58 fb Exp $
//  *************************************************************************
// 
//    FIFO Name Coding:
//    ----------------
// 
//       1:      Full
//       2:      Almost Full
//       3       Empty
//       4:      Almost Empty
// 
//  *************************************************************************

module r_fifo_1234_wl (

   reset_wclk,
   reset_rclk, 
   sw_reset_wclk,
   sw_reset_rclk,  
   wclk,
   wren,
   din,
   rclk,
   rden,
`ifdef MTIPSDPM_GEN_READEN
   mem_rden,
`endif   
   dout,
   afull,
   full,   
   aempty,
   empty,
   wlevel
`ifdef MTIPSDPM_MEM_EXT
   ,
   m_wren,
   m_waddr,
   m_wdata,
   m_rden,
   m_raddr,
   m_rdata
`endif
   );
   
parameter FF_WIDTH = 4;
parameter ADDR_WIDTH = 7;
parameter DEPTH = 128;
parameter AF_THRESHOLD = 4;
parameter AE_THRESHOLD = 3;

input   reset_wclk;
input   reset_rclk; 
input   sw_reset_wclk;
input   sw_reset_rclk;
input   wclk;                   //  Write Clock      
input   wren;                   //  Write enable     
input   [FF_WIDTH - 1:0] din;   //  Input data       
input   rclk;                   //  Read clock       
input   rden;                   //  Read enable     
`ifdef MTIPSDPM_GEN_READEN
input   mem_rden;               // memory read enable (normally same as rden)
`endif   
output  [FF_WIDTH - 1:0] dout;  //  read data        
output  afull;                  //  FIFO almost full 
output  full;                   //  FIFO full 
output  aempty;                 //  FIFO almost empty
output  empty;                  //  FIFO empty       
output  [ADDR_WIDTH - 1:0] wlevel;  // write side current level
          
`ifdef MTIPSDPM_MEM_EXT   // External Memories

output                  m_wren;    // Write enable (wclk)
output [ADDR_WIDTH-1:0] m_waddr;   // Write address (wclk)
output [FF_WIDTH-1:0]   m_wdata;   // Write data (wclk)
output                  m_rden;    // Read enable (rclk)
output [ADDR_WIDTH-1:0] m_raddr;   // Read address (rclk)
input  [FF_WIDTH-1:0]   m_rdata;   // Read data (rclk)

wire                    m_wren;
wire   [ADDR_WIDTH-1:0] m_waddr;
wire   [FF_WIDTH-1:0]   m_wdata;
wire                    m_rden;
wire   [ADDR_WIDTH-1:0] m_raddr;

`endif
                       
wire    [FF_WIDTH - 1:0] dout; 
wire    afull; 
wire    full; 
wire    aempty;
wire    empty; 
wire    [ADDR_WIDTH - 1:0] wlevel;

parameter PTR_WIDTH = ADDR_WIDTH+1;
parameter PTR_DEPTH = DEPTH*2;

wire    [PTR_WIDTH - 1:0] wr_b_ptr; 
wire    [PTR_WIDTH - 1:0] wr_g_ptr; 
reg     [PTR_WIDTH - 1:0] wr_g_ptr_reg; 
wire    [PTR_WIDTH - 1:0] rd_g_wptr;
reg     [PTR_WIDTH - 1:0] rd_b_wptr; 
wire    [PTR_WIDTH - 1:0] ptr_wck_diff; 
wire    [PTR_WIDTH - 1:0] rd_b_ptr; 
wire    [PTR_WIDTH - 1:0] rd_g_ptr; 
wire    [PTR_WIDTH - 1:0] wr_g_rptr;
wire    [PTR_WIDTH - 1:0] wr_b_rptr; 
wire    [PTR_WIDTH - 1:0] ptr_rck_diff; 
reg     afull_flag; 
reg     full_flag; 
reg     empty_flag; 
reg     aempty_flag; 
wire    [PTR_WIDTH - 1:0]  ff_rd_binval; 
wire    [PTR_WIDTH - 1:0]  ff_wr_binval;

parameter [PTR_WIDTH-1:0] R_FF_23_HEX_VAL2 = {PTR_WIDTH{1'b 0}} ;
parameter [PTR_WIDTH-1:0] R_FF_23_HEX_VAL3 = {{PTR_WIDTH-1{1'b 0}}, 1'b1} ;

`ifdef MTIPSDPM_MEM_EXT   // External Memories

    assign m_wren  = wren;                      // Write enable (wclk)
    assign m_waddr = wr_b_ptr[ADDR_WIDTH-1:0];  // Write address (wclk)
    assign m_wdata = din;                       // Write data (wclk)

    assign m_rden  = mem_rden;                  // Read enable (rclk)
    assign m_raddr = rd_b_ptr[ADDR_WIDTH-1:0];  // Read address (rclk)
    assign dout    = m_rdata;                   // Read data (rclk)

`else

    sdpm_gen #(FF_WIDTH,DEPTH,ADDR_WIDTH) U_RAM (

          .wad(wr_b_ptr[ADDR_WIDTH-1:0]),
          .din(din),
          .wclk(wclk),
          .rclk(rclk),
          .wren(wren),
        `ifdef MTIPSDPM_GEN_READEN
          .rden(mem_rden),
        `endif   
          .rad(rd_b_ptr[ADDR_WIDTH-1:0]),
          .dout(dout));

`endif
          
rgray_cnt #(PTR_WIDTH,PTR_DEPTH) U_WRT (

          .clk(wclk),
          .reset(reset_wclk),
          .sw_reset(sw_reset_wclk),
          .enable(wren),
          .b_out(wr_b_ptr),
          .g_out(wr_g_ptr));

//
// delay the write pointer before it can be captured in the read clock domain
// to ensure data is written to memory. This is needed especially if rclk is >2x wclk.
//
always @(posedge wclk or posedge reset_wclk)
   begin : ff_wrg
   if (reset_wclk == 1'b 1)
      begin
      wr_g_ptr_reg     <= {PTR_WIDTH{1'b 0}};
      end
   else
      begin
      if (sw_reset_wclk==1'b1)
         begin
              wr_g_ptr_reg     <= {PTR_WIDTH{1'b 0}};
         end
      else
         begin
              wr_g_ptr_reg     <= wr_g_ptr;
         end
      end
   end

          
rgray_cnt #(PTR_WIDTH,PTR_DEPTH) U_RD (

          .clk(rclk),
          .reset(reset_rclk),
          .sw_reset(sw_reset_rclk),
          .enable(rden),
          .b_out(rd_b_ptr),
          .g_out(rd_g_ptr));
          
assign ff_rd_binval[PTR_WIDTH-1]   = rd_g_wptr[PTR_WIDTH-1] ;
assign ff_rd_binval[PTR_WIDTH-2:0] = gray2bin(rd_g_wptr) ;

mtip_xsync #(PTR_WIDTH) U_WSYNC (

   .data_in(rd_g_ptr),
   .reset(reset_wclk),
   .clk(wclk),
   .data_s(rd_g_wptr));

always @(posedge wclk or posedge reset_wclk)
   begin : ff_rd
   if (reset_wclk == 1'b 1)
      begin
      rd_b_wptr <= {PTR_WIDTH{1'b 0}};
      end
   else
      begin
      if (sw_reset_wclk==1'b1)
         begin
         rd_b_wptr     <= {PTR_WIDTH{1'b 0}};
         end
      else
         begin
         rd_b_wptr     <= ff_rd_binval;	
         end
      end
   end

assign ptr_wck_diff = wr_b_ptr - rd_b_wptr;	

assign wlevel = ptr_wck_diff[ADDR_WIDTH-1:0];


//  Almost Full
//  -----------------------------------------
always @(posedge wclk or posedge reset_wclk)
   begin : ff_aff
   if (reset_wclk == 1'b 1)
      begin
      afull_flag <= 1'b 1;   
      end
   else
      begin
      if (ptr_wck_diff > (DEPTH - AF_THRESHOLD - 1))
         begin
         afull_flag <= 1'b 1;   
         end
      else
         begin
         afull_flag <= 1'b 0;   
         end
      end
   end

assign afull = afull_flag; 

//  Full
//  -----------------------------------------
always @(posedge reset_wclk or posedge wclk)
   begin 
   if (reset_wclk == 1'b 1)
      begin
      full_flag <= 1'b 0;	
      end
   else
      begin
      if (sw_reset_wclk == 1'b 1)
         begin
         full_flag <= 1'b 0;	
         end
      else if( ptr_wck_diff[PTR_WIDTH-1]==1'b 1 ) // extra bit indicates full // if (ptr_wck_diff > (DEPTH - 1) )
         begin
         full_flag <= 1'b 1;	
         end
      else
         begin
         full_flag <= 1'b 0;	
         end
      end
   end

assign full = full_flag; 


//  Asynchronous FIFO, Gray Pointers are Used
//  -----------------------------------------
assign ff_wr_binval[PTR_WIDTH-1]   = wr_g_rptr[PTR_WIDTH-1] ;
assign ff_wr_binval[PTR_WIDTH-2:0] = gray2bin(wr_g_rptr) ;

mtip_xsync #(PTR_WIDTH) U_RSYNC (

   .data_in(wr_g_ptr_reg),
   .reset(reset_rclk),
   .clk(rclk),
   .data_s(wr_g_rptr));

assign wr_b_rptr = ff_wr_binval;	

assign ptr_rck_diff = wr_b_rptr - rd_b_ptr;	

//  Asynchronous FIFO
//  -----------------

always @(posedge rclk or posedge reset_rclk)
   begin : ff_ef
   if (reset_rclk == 1'b 1)
      begin
      empty_flag <= 1'b 1;	
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         empty_flag <= 1'b 1;
         end
      else
         begin
         if ((rden == 1'b 1 & ptr_rck_diff == R_FF_23_HEX_VAL3) |
             (ptr_rck_diff==R_FF_23_HEX_VAL2))
            begin
            empty_flag <= 1'b 1;	
            end
         else
            begin
            empty_flag <= 1'b 0;	
            end
         end
      end
   end

assign empty = empty_flag; 

//  Almost Empty Flag Generation
//  ----------------------------

always @(posedge rclk or posedge reset_rclk)
   begin : aff_ef
   if (reset_rclk == 1'b 1)
      begin
      aempty_flag <= 1'b 1;   
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         aempty_flag <= 1'b 1;
         end
      else
         begin
                if ((rden == 1'b 1 & (ptr_rck_diff == AE_THRESHOLD)) |
                    (ptr_rck_diff < AE_THRESHOLD))
                begin
                        aempty_flag <= 1'b 1;   
                end
                else
                begin
                        aempty_flag <= 1'b 0;   
                end
         end
      end
   end

assign aempty = aempty_flag; 
   
// Gray to Binary Conversion
// -------------------------

function [PTR_WIDTH-2:0] gray2bin;

        input [PTR_WIDTH-1:0]  gray_val ;
        
        integer LOOP_IDX1; 
        integer LOOP_IDX2;
                
        for (LOOP_IDX1 = 0; LOOP_IDX1 <= PTR_WIDTH - 2; LOOP_IDX1 = LOOP_IDX1 + 1)
        begin
         
                gray2bin[LOOP_IDX1] = gray_val[LOOP_IDX1];	

                for (LOOP_IDX2 = PTR_WIDTH - 1; LOOP_IDX2 >= LOOP_IDX1 + 1; LOOP_IDX2 = LOOP_IDX2 - 1)
                begin
            
                        gray2bin[LOOP_IDX1] = gray2bin[LOOP_IDX1] ^ gray_val[LOOP_IDX2];	
            
                end
                
        end
        
endfunction

endmodule // module r_fifo_1234_wl
